package com.livestocksolutions.util;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;

/**
 * Filter out characters to only allow '+','-' [0-9] and '.' or ',' depending on system settings. 
 * @author Adam Parr
 */
public class DecimalDocumentFilter extends DocumentFilter {
	/**
	 * Default Constructor.
	 */
	public DecimalDocumentFilter(){
		super();
	}
	/**
	 * Created a Decimal filter with limits on how high and low an accepted value will be.
	 * @param min The minimum allowed value.
	 * @param max The maximum allowed value.
	 */
	public DecimalDocumentFilter(double min, double max){
		super();
		this.min=min;
		this.max=max;
	}
	/**
	 * Locale specific separator for a decimal number, eg ',' or '.' depending on region.
	 */
	private static char separator = ((DecimalFormat)NumberFormat.getInstance()).getDecimalFormatSymbols().getDecimalSeparator();
	/**
	 * Indicator if a decimal as defined in {@link #separator} has been used in this text value.
	 */
	private boolean decimal = false;
	
	/**
	 * Minimum value that will be accepted as the final value of the Document
	 */
	private double min = Double.NEGATIVE_INFINITY;
	
	/**
	 * Maximum value that will be accepted as the final value of the Document
	 */
	private double max = Double.POSITIVE_INFINITY;
	
	@Override
 	public void insertString(DocumentFilter.FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException{
		// Check all characters and see if any invalid characters are contained.
		String var = "";
		String varSign = ""/* empty for positive (+), - for negative value (-)*/;
		boolean origDec=decimal;
		for(char c:string.toCharArray()){
			if(Character.isDigit(c)||(c==separator&&!decimal)){
				if(c==separator){
					decimal=true;
				}
				var+=c;
			}
			if(c=='-'||c=='+'){
				varSign=c=='-'?"-":"+";
			}
		}
		if(!varSign.equals("")){
			varSign=varSign.equals("+")?"":varSign;
			double check=Double.valueOf(fb.getDocument().getText(0, fb.getDocument().getLength()));
			check=check>0&&varSign.equals("-")||check<0&&varSign.equals("")?check*-1:check;
			if(min<check&&check<max){
				if(fb.getDocument().getText(0, 1).contains("-")){
					fb.replace(0,1,varSign,null);
				}else{
					fb.insertString(0,varSign,null);
				}
			}
		}
		
		String value=fb.getDocument().getText(0, fb.getDocument().getLength());
		fb.insertString(offset,var,attr);
		// Check if the value now is over the maximum or under the minimum
		double check=Double.valueOf(fb.getDocument().getText(0, fb.getDocument().getLength()));
		if(min>check||check>max){
			// Change the value back to what it was before
			fb.replace(0,fb.getDocument().getLength(),value,null);
			decimal = origDec;
		}
	}
	
	@Override
	public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
		decimal=fb.getDocument().getText(offset, length).contains(String.valueOf(separator))?false:decimal;
		super.remove(fb, offset, length);
	}
	
	@Override
	public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String string, AttributeSet attr) throws BadLocationException{
		// Check all characters and see if any invalid characters are contained.
		String var = "";
		String varSign = ""/* empty for positive (+), - for negative value (-)*/;
		decimal=fb.getDocument().getText(offset, length).contains(String.valueOf(separator))?false:decimal;
		boolean origDec=decimal;
		for(char c:string.toCharArray()){
			if(Character.isDigit(c)||(c==separator&&!decimal)){
				if(c==separator){
					decimal=true;
				}
				var+=c;
			}
			if(c=='-'||c=='+'){
				varSign=c=='-'?"-":"+";
			}
		}
		if(!varSign.equals("")){
			varSign=varSign.equals("+")?"":varSign;
			double check=Double.valueOf(varSign+fb.getDocument().getText(0, fb.getDocument().getLength()));
			check=check>0&&varSign.equals("-")||check<0&&varSign.equals("")?check*-1:check;
			if(min<check&&check<max){
				if(fb.getDocument().getText(0, 1).contains("-")){
					fb.replace(0,1,varSign,null);
				}else{
					fb.insertString(0,varSign,null);
				}
			}
		}
		if(var.equals("")){
			// Do not replace text if there is nothing to replace it with
			return;
		}
		
		String value=fb.getDocument().getText(0, fb.getDocument().getLength());
		fb.replace(offset,length,var,attr);
		// Check if the value now is over the maximum or under the minimum
		double check=Double.valueOf(fb.getDocument().getText(0, fb.getDocument().getLength()));
		if(min>check||check>max){
			// Change the value back to what it was before
			fb.replace(0,fb.getDocument().getLength(),value,attr);
			decimal = origDec;
		}
	}
	
	/**
	 * Set the minimum allowed value. Default is {@link Double#NEGATIVE_INFINITY}.
	 * @param min value that can not be exceeded.
	 */
	public void setMinimum(double min){
		this.min=min;
	}
	
	/**
	 * Set the maximum allowed value. Default is {@link Double#POSITIVE_INFINITY}.
	 * @param max value that can not be exceeded.
	 */
	public void setMaximum(double max){
		this.max=max;
	}
	
	/**
	 * Get the maximum allowed value.
	 * @return The maximum allowed value.
	 */
	public double getMinimum(){
		return min;
	}
	
	/**
	 * Get the minimum allowed value.
	 * @return The minimum allowed value.
	 */
	public double getMaximum(){
		return max;
	}
}
